/********************************************
 * 功能说明: Redis工具类
 * 模块名称: Redis模块
 * 系统名称: java敏捷开发通用平台
 * 软件版权:
 * 系统版本: 1.0.0
 * 开发人员: zhaorq
 * 开发时间: 2017年11月20日 上午11:41:24
 * 审核人员:
 * 相关文档:
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/
package com.hyacinth.utils.redis;

import com.hyacinth.utils.serialize.KryoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;

import java.util.*;

/**
 * Redis工具类
 * @author zhaorq
 * @version 1.0.0.1
 */
public class RedisUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);
	
	/** Jedis连接池 */
	private static JedisPool jedisPool = null;
	private static List<JedisPool> jedisPoolList = new ArrayList<JedisPool>();
	
	public static void main(String[] args) {
		init();

		// 创建消息
		String userMsg = "支付宝到账2元";
		
		// 存储数据
		RedisUtils.hset("userMsg", "userMsg", userMsg);
        
		// 获取数据
		userMsg = RedisUtils.hget("userMsg", "userMsg");
		logger.debug("获取数据：" + userMsg);
		
		RedisUtils.del("userMsg");
		userMsg = RedisUtils.hget("userMsg", "userMsg");
		logger.debug("获取数据：" + userMsg);
	}
    
    public static JedisPool getJedisPool() {
		return jedisPool;
	}

	public static void setJedisPool(JedisPool jedisPool) {
		RedisUtils.jedisPool = jedisPool;
	}
	
	public static List<JedisPool> getJedisPoolList() {
		return jedisPoolList;
	}

	public static void setJedisPoolList(List<JedisPool> jedisPoolList) {
		RedisUtils.jedisPoolList = jedisPoolList;
	}

	public static void init() {
		JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(500);
        config.setMaxIdle(5);
        config.setMaxWaitMillis(1000*10);
        config.setTestOnBorrow(true);
        jedisPool = new JedisPool(config, "192.168.1.196", 6379, 10000, "yinfeng2012");
        jedisPoolList.add(jedisPool);
	}

	/**
     * 获得Jedis实例
     * @return Jedis实例
     */
    public static Jedis getResource(JedisPool jedisPool) {
    	Jedis jedis = null;
		try {
			jedis = jedisPool.getResource();
			if (!jedis.isConnected()) {
				logger.error("获取jedis连接失败，jedisPool=" + jedisPool);
			}
			return jedis;
		} catch (Exception e) {
			logger.error("获取jedis链接异常，jedisPool=" + jedisPool, e);
		}
		return null;
	}
	
	/**
	 * 获取哈希键值
	 * @param key
	 * @param field
	 * @return
	 */
	public static String hget(String key, String field) {
		for (JedisPool jedisPool : jedisPoolList) {
			Jedis jedis = getResource(jedisPool);
			try {
				if (jedis != null) {
					return jedis.hget(key, field);
				}
			} finally {
				if (jedis != null) {
					jedis.close();
				}
			}
		}
		return null;
	}
	
	/**
	 * 获取哈希键值对象
	 * @param key
	 * @param field
	 * @param clazz
	 * @return
	 */
	public static <T> T hget(String key, String field, Class<T> clazz) {
		String value = hget(key, field);
		if (value != null) {
			return KryoUtils.readObjectFromString(value, clazz);
		}
		return null;
	}
	
	/**
	 * 获取哈希所有键值
	 * @param key
	 * @return
	 */
	public static Map<String, String> hgetAll(String key) {
		for (JedisPool jedisPool : jedisPoolList) {
			Jedis jedis = getResource(jedisPool);
			try {
				if (jedis != null) {
					return jedis.hgetAll(key);
				}
			} finally {
				if (jedis != null) {
					jedis.close();
				}
			}
		}
		return null;
	}
	
	/**
	 * 获取哈希所有键值
	 * @param key
	 * @return
	 */
	public static <T> List<T> hgetAll(String key, Class<T> clazz) {
		List<T> list = new ArrayList<T>();
		Map<String, String> map = hgetAll(key);
		if (map != null && !map.isEmpty()) {
			for (Map.Entry<String, String> entry : map.entrySet()) {
				list.add(KryoUtils.readObjectFromString(entry.getValue(), clazz));
			}
		}
		return list;
	}

	/**
	 * 添加哈希键值
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 */
	public static Long hset(String key, String field, String value) {
		for (JedisPool jedisPool : jedisPoolList) {
			Jedis jedis = getResource(jedisPool);
			try {
				if (jedis != null) {
					jedis.hset(key, field, value);
				}
			} finally {
				if (jedis != null) {
					jedis.close();
				}
			}
		}
		return null;
	}

	/**
	 * 添加哈希键值与指定过期时间
	 * @param key 键值
	 * @param field 域名
	 * @param value 值
	 * @return 添加结果
	 */
	public static Long hset(String key, String field, String value, Date expired) {
		for (JedisPool jedisPool : jedisPoolList) {
			Jedis jedis = getResource(jedisPool);
			try {
				if (jedis != null) {
					jedis.hset(key, field, value);
					jedis.expireAt(key, expired.getTime() / 1000);
				}
			} finally {
				if (jedis != null) {
					jedis.close();
				}
			}
		}
		return null;
	}

	/**
	 * 添加哈希键值对象
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 */
	public static Long hset(String key, String field, Object value) {
		return hset(key, field, KryoUtils.writeObjectToString(value));
	}
	
	/**
	 * 删除哈希键值
	 * @param key
	 * @param field
	 * @return
	 */
	public static Long hdel(String key, String field) {
		for (JedisPool jedisPool : jedisPoolList) {
			Jedis jedis = getResource(jedisPool);
			try {
				if (jedis != null) {
					jedis.hdel(key, field);
				}
			} finally {
				if (jedis != null) {
					jedis.close();
				}
			}
		}
		return null;
	}
	
	/**
	 * 获取哈希所有键
	 * @param key
	 * @return
	 */
	public static Set<String> hkeys(String key) {
		for (JedisPool jedisPool : jedisPoolList) {
			Jedis jedis = getResource(jedisPool);
			try {
				if (jedis != null) {
					return jedis.hkeys(key);
				}
			} finally {
				if (jedis != null) {
					jedis.close();
				}
			}
		}
		return null;
	}
	
	/**
	 * 删除键
	 * @param key
	 * @return
	 */
	public static Long del(String key) {
		for (JedisPool jedisPool : jedisPoolList) {
			Jedis jedis = getResource(jedisPool);
			try {
				if (jedis != null) {
					jedis.del(key);
				}
			} finally {
				if (jedis != null) {
					jedis.close();
				}
			}
		}
		return null;
	}
	
	/**
	 * 加锁
	 * @param lockName 锁的key
	 * @return 锁标识
	 */
	public static String lockWithTimeout(String lockName) {
		return lockWithTimeout(jedisPoolList.get(0), lockName, 5000, 3000);
	}
	
	/**
	 * 释放锁
	 * @param lockName 锁的key
	 * @param identifier 释放锁的标识
	 * @return
	 */
	public static boolean releaseLock(String lockName, String identifier) {
		return releaseLock(jedisPoolList.get(0), lockName, identifier);
	}

	/**
	 * 加锁
	 * @param lockName 锁的key
	 * @param acquireTimeout 获取超时时间
	 * @param timeout 锁的超时时间
	 * @return 锁标识
	 */
	public static String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
		return lockWithTimeout(jedisPoolList.get(0), lockName, acquireTimeout, timeout);
	}
	
	/**
	 * 加锁
	 * @param lockName 锁的key
	 * @param acquireTimeout 获取超时时间
	 * @param timeout 锁的超时时间
	 * @return 锁标识
	 */
	public static String lockWithTimeout(JedisPool jedisPool, String lockName, long acquireTimeout, long timeout) {
		Jedis conn = null;
		String retIdentifier = null;
		try {
			// 获取连接
			conn = getResource(jedisPool);
			// 随机生成一个value
			String identifier = UUID.randomUUID().toString();
			// 锁名，即key值
			String lockKey = "lock:" + lockName;
			// 超时时间，上锁后超过此时间则自动释放锁
			int lockExpire = (int) (timeout / 1000);
			
			// 获取锁的超时时间，超过这个时间则放弃获取锁
			long end = System.currentTimeMillis() + acquireTimeout;
			while (System.currentTimeMillis() < end) {
				if (conn.setnx(lockKey, identifier) == 1) {
					conn.expire(lockKey, lockExpire);
					// 返回value值，用于释放锁时间确认
					retIdentifier = identifier;
					return retIdentifier;
				}
				// 返回-1代表key没有设置超时时间，为key设置一个超时时间
				if (conn.ttl(lockKey) == -1) {
					conn.expire(lockKey, lockExpire);
				}
				
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					Thread.currentThread().interrupt();
				}
			}
		} catch (JedisException e) {
			logger.error("redis加锁异常, lockName={}", lockName, e);
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
		return retIdentifier;
	}
	
	/**
	 * 释放锁
	 * @param lockName 锁的key
	 * @param identifier 释放锁的标识
	 * @return
	 */
	public static boolean releaseLock(JedisPool jedisPool, String lockName, String identifier) {
		Jedis conn = null;
		String lockKey = "lock:" + lockName;
		boolean retFlag = false;
		try {
			conn = getResource(jedisPool);
			while (true) {
				// 监视lock，准备开始事务
				conn.watch(lockKey);
				// 通过前面返回的value值判断是不是该锁，若是该锁，则删除，释放锁
				if (identifier.equals(conn.get(lockKey))) {
					Transaction transaction = conn.multi();
					transaction.del(lockKey);
					List<Object> results = transaction.exec();
					if (results == null) {
						continue;
					}
					retFlag = true;
				}
				conn.unwatch();
				break;
			}
		} catch (JedisException e) {
			logger.error("redis释放锁异常, lockName={}", lockName, e);
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
		return retFlag;
	}
	
}
